/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2013.06.02                                          *
 *                                                                       *
 *************************************************************************/

#ifndef FreeMAPs_include_COMMON_base_StateIndicator_h
#define FreeMAPs_include_COMMON_base_StateIndicator_h

#include "FreeMAPsConfig.h"

//C++ headers
#include <string>
#include <map>

//COMMON headers
#include "COMMON/base/Common.h"
#include "COMMON/enum/State.h"

namespace FreeMAPs
{

//forward declarations
class InputDatabase;

/**
 * StateIndicator will be used as the base class that has
 * a state indicator to indicate whether the object is prepared or not.
 * and if it is not prepared, error will be reported when calling it's
 * member function.
 *
 * when a objection is derived from this object, DO NOT set it's
 * state to GOOD at constructor function, if doing so, the state will lose
 * it's function.
 * usually change it state by means: get_from_input().
 * this is not a forced manner, it is a recommend manner.
 */
class StateIndicator
{
public:

    virtual
    ~StateIndicator();

protected:

    /**
     * full name = parent_name + "-->" + object_name
     * if is_registered_in_info_manager, then InfoManager will check it by calling check_consistence().
     */
    explicit
    StateIndicator(
        const std::string& parent_name,
        const std::string& object_name,
        const bool is_registered_in_info_manager = false);

public:

    inline void
    clear();

    inline const std::string&
    get_object_name() const;

    inline const std::string&
    get_full_name() const;

    inline bool
    is_registered_in_info_manager() const;

    inline State
    get_state() const;

    /**
     * add a key that should be checked in future.
     * the main work is to check if all it's refered object is exists.
     */
    inline void
    add_key_for_checking_consistence(
        const std::string& key,
        const std::string& message) const;

    /**
     * if a object is checked, let the object know this.
     */
    inline void
    finish_checking_consistence(
        const std::string& key) const;

    /**
     * @return true if the object requires check the consistence.
     */
    inline bool
    require_check_consistence()const;

    inline bool
    require_rebuilding() const;

    inline void
    finish_rebuilding() const;

    /**
     * update the state
     */
    inline void
    update_state (
        const State state) const;

    /**
     * some times, one want to know the object's class information.
     * we do NOT use RTTI, wo use this virtual function to tell us
     * if the object is derived from AnalyticFunction or not.
     */
    inline virtual bool
    is_analytic_function() const;

    /**
     * if not active(d_verbose_level<0), one can NOT show verbose info.
     * @note there has a special class of CommonInfo->verbose_level,
     * it will be used as other's default verbose level.
     * so when active, we use a level to identity the level.
     * if level = 2, then it is CommonInfo
     * otherwise level = 0.
     */
    inline void
    active_verbose_mode(
        const int level);

    /**
     * make sure it has been activated.
     *
     * The level to output information.
     * the higher the level, the more information will be output.
     * the higher level will display all the information of the lower level.
     *  -0: no output at all.
     *  -1: output warning message.
     *  -2: output main progress schedule.
     *  -3: output cost progress schedule.
     *  -4: output all information.
     */
    inline int
    get_verbose_level() const;

public:

    /**
     * only those object that rely on mesh should be required whether prepared or not.
     */
    bool
    is_prepared() const;

    /**
     * only those object that rely on mesh should be checked whether prepared or not.
     */
    void
    check_state()const;

    /**
     * attach database to this object so that when checking in future, it can report
     * where error is.
     */
    void
    attach_database (
        const InputDatabase* db);

    const InputDatabase*
    get_database() const;

    /**
     * @return the location where these information is read from.
     * this is mainly used in report error.
     */
    std::string
    get_location() const;

    /**
     * @return ture if the d_verbose_level is greater than required_least_level
     */
    bool
    require_show_verbose_info(
        const int required_least_level) const;

    /**
     * output info to log file if d_verbose_level is greater than required_least_level.
     * the required_least_level is set as:
     *  -0: no output at all.
     *  -1: output warning message.
     *  -2: output main progress schedule.
     *  -3: output cost progress schedule.
     *  -4: output all information.
     * This is the global option, other place may set the level higher or lower locally.
     * So when set verbose_level other place(if supported),
     * then the global level will be replaced locally.
     * default to 2
     */
    void
    show_verbose_info(
        const std::string& info,
        const int required_least_level) const;

    /**
     * check the consistence, the main work should be done on derived class.
     */
    virtual void
    check_consistence() const;

    /**
     * check if all keys have been checked the consistence.
     */
    void
    check_all_consistence() const;

protected:

    /**
     * before calling get_from_input(), calling this to do some common work
     */
    void
    prepare_for_use(
        const InputDatabase* db = NULL);

    /**
     * when prepare for reading from parent database, DO NOT call get_from_input()
     * any more because it will be done in the parent database.
     */
    void
    prepare_for_use_from_parent_database(
        const InputDatabase* parent_db);

    /**
     * a object derived from this will have their own get_from_input(),
     * this function get the putlic object.
     */
    void
    get_common_from_input (
        const InputDatabase* db);

private:

    /*
     * use name and full name to identity a object.
     * full name has the information where it is.
     * for example, a equation named thermal_equation in equation ensemble of navier_stokes_equation_ensemble
     * will hava a name like "thermal_equation" and full_name like:
     * "...__navier_stokes_equation_ensemble__thermal_equation".
     */
    std::string d_full_name;
    std::string d_object_name;

    bool d_is_registered_in_info_manager;

    mutable State d_state;

    /*
     * if d_verbose_level < 0, then it is not work, and the user can not use it to show verbose info.
     * if one want to use it, use active_verbose_mode() to active it,
     * and the d_verbose_level will be set to 0.
     */
    int d_verbose_level;

    /*
     * this is an assist tools.
     * when build the object, maybe some other objects that it refers are not prepared,
     * so if checking at that time will cause error.
     * now we will check the consistence after all objects are build.
     * but the program may forget to check some objects.
     * so use this to save all the objects that require to check at checking stage.
     *
     * map <key_name, pair<has_been_checked, message> >
     * note that this object do NOT manager the memory of the pointers.
     */
    mutable std::map<std::string, std::pair<bool, std::string> > d_keys_to_be_checked_consistence;

    /*
     * the database that this object read information from.
     * do not manager the memory.
     * do not use the shared_ptr to avoid the memory in destructor stage.
     */
    const InputDatabase* d_database;

private:

    DISALLOW_COPY_AND_ASSIGN (StateIndicator);
};

} //namespace FreeMAPs

#include "COMMON/base/StateIndicator.inl"

#endif
